package com.hzw.saas.common.flow.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import javax.annotation.Resource;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzw.saas.api.user.IUserAccountService;
import com.hzw.saas.api.user.bo.UserAccountBO;
import com.hzw.saas.common.config.exception.DefaultSaasException;
import com.hzw.saas.common.config.util.AssertUtil;
import com.hzw.saas.common.config.util.PageParam;
import com.hzw.saas.common.config.util.SpringContextUtils;
import com.hzw.saas.common.flow.mapper.FlowInstanceMapper;
import com.hzw.saas.common.flow.pojo.bo.FlowInstanceBO;
import com.hzw.saas.common.flow.pojo.bo.FlowProcessBO;
import com.hzw.saas.common.flow.pojo.data.UserInfo;
import com.hzw.saas.common.flow.pojo.enums.FlowNodeStatus;
import com.hzw.saas.common.flow.pojo.enums.FlowStatus;
import com.hzw.saas.common.flow.pojo.model.*;
import com.hzw.saas.common.flow.service.*;
import com.hzw.saas.common.util.MapperUtil;
import com.hzw.saas.common.util.SnowFlakeUtil;
import com.hzw.saas.common.util.enums.IsEnum;
import cn.hutool.core.util.StrUtil;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * <p>
 * 流程实例实现类
 * </p>
 *
 * @author sonam
 * @since 2021-08-04
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class FlowInstanceServiceImpl extends ServiceImpl<FlowInstanceMapper, FlowInstance> implements IFlowInstanceService {

    @Resource
    private IUserAccountService userAccountService;
    @Resource
    private IFlowService flowService;
    @Resource
    private IFlowNodeService flowNodeService;
    @Resource
    private IFlowProcessService flowProcessService;
    @Resource
    private IFlowProcessRecordService flowProcessRecordService;

    @Override
    public IPage<FlowInstanceBO> pageFlowInstance(PageParam pageParam, String userId, String flowId) {
        IPage<FlowInstance> pageDO = this.pageFlowInstanceDO(pageParam, userId, flowId);
        IPage<FlowInstanceBO> pageBO = new Page<>(pageDO.getCurrent(), pageDO.getSize(), pageDO.getTotal());
        pageBO.setRecords(new ArrayList<>());
        for (FlowInstance instanceDO : pageDO.getRecords()) {
            FlowInstanceBO instanceBO = this.convert(instanceDO, userId);
            pageBO.getRecords().add(instanceBO);
        }
        return pageBO;
    }

    public IPage<FlowInstance> pageFlowInstanceDO(PageParam pageParam, String userId, String flowId) {
        return super.lambdaQuery()
            .eq(StrUtil.isNotBlank(userId), FlowInstance::getSubmitUserId, userId)
            .eq(StrUtil.isNotBlank(flowId), FlowInstance::getFlowId, flowId)
            .page(pageParam.convert());
    }

    public List<FlowInstance> listFlowInstanceDO(String userId, String flowId) {
        return super.lambdaQuery()
            .eq(StrUtil.isNotBlank(userId), FlowInstance::getSubmitUserId, userId)
            .eq(StrUtil.isNotBlank(flowId), FlowInstance::getFlowId, flowId)
            .list();
    }

    @Override
    public FlowInstance createFlowInstance(String userId, String flowId, String flowInstanceName) {
        // 查询流程ID
        Flow flow = flowService.getById(flowId);
        AssertUtil.assertThrow("流程不存在", Objects.isNull(flow));

        FlowNode startNode = flowNodeService.getStartNode(flowId);
        FlowInstance flowInstance = new FlowInstance();
        flowInstance.setPid(SnowFlakeUtil.nextIdStr());
        flowInstance.setFlowId(flowId);
        flowInstance.setFlowInstanceName(StrUtil.isNotBlank(flowInstanceName) ? flowInstanceName : flow.getFlowName());
        flowInstance.setFlowNodeId(startNode.getPid());
        flowInstance.setState(FlowStatus.PROCESS.code());
        flowInstance.setSubmitUserId(userId);
        this.save(flowInstance);

        return flowInstance;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public FlowInstance doFlowProcess(String flowInstanceId, String flowProcessId, String userId, String suggestion) {
        return doFlowProcess(this.getById(flowInstanceId), flowProcessId, userId, suggestion);
    }

    public FlowInstance doFlowProcess(FlowInstance flowInstance, String flowProcessId, String userId, String suggestion) {
        AssertUtil.assertThrow("流程实例丢失", HttpStatus.NOT_FOUND, Objects.isNull(flowInstance));
        // 用户权限
        UserInfo userInfo = new UserInfo(userId, null);
        // 校验flowProcessId的权限
        FlowProcess flowProcess = flowProcessService.getNextFlowProcess(flowInstance, flowProcessId, userInfo);
        AssertUtil.assertThrow("当前节点无此处理过程或无权限！", HttpStatus.FORBIDDEN, Objects.isNull(flowProcess));
        // 下一个节点
        FlowNode nextFlowNode = flowNodeService.getById(flowProcess.getFlowNodeNextId());
        AssertUtil.assertThrow("内部错误，无法获取下一节点", HttpStatus.INTERNAL_SERVER_ERROR, Objects.isNull(nextFlowNode));
        // 处理过程记录
        FlowProcessRecord flowProcessRecord = new FlowProcessRecord();
        flowProcessRecord.setPid(SnowFlakeUtil.nextIdStr());
        flowProcessRecord.setFlowProcessId(flowProcessId);
        flowProcessRecord.setFlowInstanceId(flowInstance.getPid());
        flowProcessRecord.setApprover(userId);
        flowProcessRecord.setSuggestion(StrUtil.isBlank(suggestion) ? flowProcess.getDefaultComment() : suggestion);
        flowProcessRecordService.save(flowProcessRecord);
        // 更新流程实例状态
        flowInstance.setFlowNodeId(nextFlowNode.getPid());
        flowInstance.setState(Objects.equals(nextFlowNode.getFlowNodeStatus(), FlowNodeStatus.END_NODE.code()) ?
            FlowStatus.COMPLETE.code() : FlowStatus.PROCESS.code());
        flowInstance.pureUpdate();
        super.updateById(flowInstance);
        // 处理业务逻辑，获取对应的流程模版数据
        FlowActionHandle flowActionHandle = this.getFlowActionHandle(flowProcess.getActions());
        // TODO: ZZL@08/12/2021 手动事务控制异常状态
        try {
            flowActionHandle.handle(flowInstance, flowProcessRecord);
        } catch (Exception e) {
            throw DefaultSaasException.build(e.getMessage());
        }
        return flowInstance;
    }

    private FlowActionHandle getFlowActionHandle(String actions) {
        FlowActionHandle flowActionHandle;
        if (StrUtil.isNotBlank(actions)) {
            AssertUtil.assertThrow(StrUtil.format("处理流程节点业务{}异常", actions), HttpStatus.INTERNAL_SERVER_ERROR, !SpringContextUtils.containsBean(actions));
            flowActionHandle = SpringContextUtils.getBean(actions, FlowActionHandle.class);
        } else {
            flowActionHandle = (flowInstance, flowRecord) -> log.debug("处理节点业务: {}", flowRecord.getFlowInstanceId());
        }
        return flowActionHandle;
    }

    private FlowInstanceBO convert(FlowInstance instanceDO, String userId) {
        FlowInstanceBO instanceBO = this.convert(instanceDO);
        // 装配流程当前节点处理
        List<FlowProcess> flowProcesses = flowProcessService.listNextFlowProcesses(instanceDO,
            new UserInfo(userId, null), IsEnum.TRUE.getCode());
        instanceBO.setCurrFlowProcesses(MapperUtil.nf().mapAsList(flowProcesses, FlowProcessBO.class));
        return instanceBO;
    }

    private FlowInstanceBO convert(FlowInstance instanceDO) {
        FlowInstanceBO instanceBO = MapperUtil.nf().map(instanceDO, FlowInstanceBO.class);
        // 装配流程定义信息
        Optional<Flow> flowOptional = Optional.ofNullable(flowService.getById(instanceDO.getFlowId()));
        instanceBO.setFlowName(flowOptional.map(Flow::getFlowName).orElse(""));
        instanceBO.setFlowDesc(flowOptional.map(Flow::getFlowDesc).orElse(""));
        if (StrUtil.isBlank(instanceBO.getFlowInstanceName())) {
            instanceBO.setFlowInstanceName(flowOptional.map(Flow::getFlowName).orElse(""));
        }
        // 装配流程节点定义信息
        Optional<FlowNode> flowNodeOptional = Optional.ofNullable(flowNodeService.getById(instanceDO.getFlowNodeId()));
        instanceBO.setFlowNodeName(flowNodeOptional.map(FlowNode::getFlowNodeName).orElse(""));
        instanceBO.setFlowNodeStyle(flowNodeOptional.map(FlowNode::getFlowNodeStyle).orElse(""));
        // 装配发起人信息
        Optional<UserAccountBO> accountOptional = Optional.ofNullable(userAccountService.getAccount(instanceDO.getSubmitUserId()));
        instanceBO.setSubmitUserName(accountOptional.map(UserAccountBO::getUserName).orElse(""));
        return instanceBO;
    }

}
